Spatial Weights

Spatial weights are mathematical structures used to represent spatial relationships. They characterize the relationship of each observation to every other observation using some concept of proximity or closeness that depends on the weight type.

They can be build in PySAL from shapefiles, as well as some types of files.


In [1]:
import pysal as ps
import numpy as np

There are functions to construct weights directly from a file path.


In [2]:
shp_path = ps.examples.get_path('NAT.shp')

Weight Types

Contiguity:

Queen Weights

A commonly-used type of weight is a queen contigutiy weight, which reflects adjacency relationships as a binary indicator variable denoting whether or not a polygon shares an edge or a verted each another polygon. These weights are symmetric, in that when polygon $A$ neighbors polygon $B$, both $w_{AB} = 1$ and $w_{BA} = 1$.

To construct queen weights from a shapefile, use the queen_from_shapefile function:


In [3]:
qW = ps.queen_from_shapefile(shp_path)
dataframe = ps.pdio.read_files(shp_path)

In [4]:
qW


Out[4]:
<pysal.weights.weights.W at 0x7f30f6e1ab90>

All weights objects have a few traits that you can use to work with the weights object, as well as to get information about the weights object.

To get the neighbors & weights around an observation, use the observation's index on the weights object, like a dictionary:


In [5]:
qW[4] #neighbors & weights of the 5th observation


Out[5]:
{2: 1.0, 5: 1.0, 28: 1.0, 62: 1.0}

By default, the weights and the pandas dataframe will use the same index. So, we can view the observation and its neighbors in the dataframe by putting the observation's index and its neighbors' indexes together in one list:


In [6]:
self_and_neighbors = [4]
self_and_neighbors.extend(qW.neighbors[4])
print(self_and_neighbors)


[4, 2, 28, 5, 62]

and grabbing those elements from the dataframe:


In [7]:
dataframe.loc[self_and_neighbors]


Out[7]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
2 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
28 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
5 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
62 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...

5 rows × 70 columns

A full, dense matrix describing all of the pairwise relationships is constructed using the .full method, or when pysal.full is called on a weights object:


In [8]:
Wmatrix, ids = qW.full()
#Wmatrix, ids = ps.full(qW)

In [9]:
Wmatrix


Out[9]:
array([[ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  1., ...,  0.,  0.,  0.],
       [ 0.,  1.,  0., ...,  0.,  0.,  0.],
       ..., 
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.]])

Note that this matrix is binary, in that its elements are either zero or one, since an observation is either a neighbor or it is not a neighbor.

However, many common use cases of spatial weights require that the matrix is row-standardized. This is done simply in PySAL using the .transform attribute


In [10]:
qW.transform = 'r'

Now, if we build a new full matrix, its rows should sum to one:


In [11]:
Wmatrix, ids = qW.full()

In [12]:
Wmatrix.sum(axis=1) #numpy axes are 0:column, 1:row, 2:facet, into higher dimensions


Out[12]:
array([ 1.,  1.,  1., ...,  1.,  1.,  1.])

Since weight matrices are typically very sparse, there is also a sparse weights matrix constructor:


In [13]:
qW.sparse


Out[13]:
<3085x3085 sparse matrix of type '<type 'numpy.float64'>'
	with 18168 stored elements in Compressed Sparse Row format>

By default, PySAL assigns each observation an index according to the order in which the observation was read in. This means that, by default, all of the observations in the weights object are indexed by table order. If you have an alternative ID variable, you can pass that into the weights constructor.

For example, the NAT.shp dataset has a possible alternative ID Variable, a FIPS code.


In [14]:
dataframe.head()


Out[14]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
0 Lake of the Woods Minnesota 27 077 27077 27 77 27077 0 0.000000 ... 0.024534 0.285235 0.372336 0.342104 0.336455 11.279621 5.4 5.663881 9.515860 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
1 Ferry Washington 53 019 53019 53 19 53019 0 0.000000 ... 0.317712 0.256158 0.360665 0.361928 0.360640 10.053476 2.6 10.079576 11.397059 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
2 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
3 Okanogan Washington 53 047 53047 53 47 53047 0 2.612330 ... 0.155922 0.258540 0.371218 0.381240 0.394519 9.039900 8.1 10.084926 12.840340 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...

5 rows × 70 columns

The observation we were discussing above is in the fifth row: Pend Oreille county, Washington. Note that its FIPS code is 53051.

Then, instead of indexing the weights and the dataframe just based on read-order, use the FIPS code as an index:


In [15]:
qW = ps.queen_from_shapefile(shp_path, idVariable='FIPS')

Now, Pend Oreille county has a different index:


In [16]:
qW[4] #fails, since no FIPS is 4.


---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-16-1d8a3009bc1e> in <module>()
----> 1 qW[4] #fails, since no FIPS is 4.

/home/ljw/.local/lib/python2.7/site-packages/pysal/weights/weights.pyc in __getitem__(self, key)
    504         {1: 1.0, 4: 1.0, 101: 1.0, 85: 1.0, 5: 1.0}
    505         """
--> 506         return dict(zip(self.neighbors[key], self.weights[key]))
    507 
    508     def __iter__(self):

KeyError: 4

Note that a KeyError in Python usually means that some index, here 4, was not found in the collection being searched, the IDs in the queen weights object. This makes sense, since we explicitly passed an idVariable argument, and nothing has a FIPS code of 4.

Instead, if we use the observation's FIPS code:


In [17]:
qW['53051']


Out[17]:
{u'16017': 1.0, u'16021': 1.0, u'53063': 1.0, u'53065': 1.0}

We get what we need.

In addition, we have to now query the dataframe using the FIPS code to find our neighbors. But, this is relatively easy to do, since pandas will parse the query by looking into python objects, if told to.

First, let us store the neighbors of our target county:


In [18]:
self_and_neighbors = ['53051']
self_and_neighbors.extend(qW.neighbors['53051'])

Then, we can use this list in .query:


In [19]:
dataframe.query('FIPS in @self_and_neighbors')


Out[19]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
2 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
5 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
28 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
62 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...

5 rows × 70 columns

Note that we have to use @ before the name in order to show that we're referring to a python object and not a column in the dataframe.


In [20]:
#dataframe.query('FIPS in neighs') will fail because there is no column called 'neighs'

Of course, we could also reindex the dataframe to use the same index as our weights:


In [21]:
fips_frame = dataframe.set_index(dataframe.FIPS)
fips_frame.head()


Out[21]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
FIPS
27077 Lake of the Woods Minnesota 27 077 27077 27 77 27077 0 0.000000 ... 0.024534 0.285235 0.372336 0.342104 0.336455 11.279621 5.4 5.663881 9.515860 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53019 Ferry Washington 53 019 53019 53 19 53019 0 0.000000 ... 0.317712 0.256158 0.360665 0.361928 0.360640 10.053476 2.6 10.079576 11.397059 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53065 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53047 Okanogan Washington 53 047 53047 53 47 53047 0 2.612330 ... 0.155922 0.258540 0.371218 0.381240 0.394519 9.039900 8.1 10.084926 12.840340 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53051 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...

5 rows × 70 columns

Now that both are using the same weights, we can use the .loc indexer again:


In [22]:
fips_frame.loc[self_and_neighbors]


Out[22]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
FIPS
53051 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53065 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
16017 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
16021 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53063 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...

5 rows × 70 columns

Rook Weights

Rook weights are another type of contiguity weight, but consider observations as neighboring only when they share an edge. The rook neighbors of an observation may be different than its queen neighbors, depending on how the observation and its nearby polygons are configured.

We can construct this in the same way as the queen weights, using the special rook_from_shapefile function


In [23]:
rW = ps.rook_from_shapefile(shp_path, idVariable='FIPS')

In [24]:
rW['53051']


Out[24]:
{u'16017': 1.0, u'16021': 1.0, u'53063': 1.0, u'53065': 1.0}

These weights function exactly like the Queen weights, and are only distinguished by what they consider "neighbors."

Bishop Weights

In theory, a "Bishop" weighting scheme is one that arises when only polygons that share vertexes are considered to be neighboring. But, since Queen contiguigy requires either an edge or a vertex and Rook contiguity requires only shared edges, the following relationship is true:

$$ \mathcal{Q} = \mathcal{R} \cup \mathcal{B} $$

where $\mathcal{Q}$ is the set of neighbor pairs via queen contiguity, $\mathcal{R}$ is the set of neighbor pairs via Rook contiguity, and $\mathcal{B}$ via Bishop contiguity. Thus:

$$ \mathcal{Q} \setminus \mathcal{R} = \mathcal{B}$$

Bishop weights entail all Queen neighbor pairs that are not also Rook neighbors.

PySAL does not have a dedicated bishop weights constructor, but you can construct very easily using the w_difference function. This function is one of a family of tools to work with weights, all defined in ps.weights, that conduct these types of set operations between weight objects.


In [25]:
bW = ps.w_difference(qW, rW, constrained=False, silent_island_warning=True) #silence because there will be a lot of warnings

In [26]:
bW.histogram


Out[26]:
[(0, 2359), (1, 531), (2, 150), (3, 31), (4, 14)]

Thus, the vast majority of counties have no bishop neighbors. But, a few do. A simple way to see these observations in the dataframe is to find all elements of the dataframe that are not "islands," the term for an observation with no neighbors:


In [27]:
islands = bW.islands

In [28]:
dataframe.query('FIPS not in @islands')


Out[28]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
32 Williams North Dakota 38 105 38105 38 105 38105 0 0.000000 ... 0.085191 0.253422 0.331675 0.336543 0.362336 9.133037 8.300000 8.312172 9.884332 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
35 Roosevelt Montana 30 085 30085 30 85 30085 0 5.682948 ... 0.154560 0.304054 0.376976 0.381225 0.391364 12.080537 11.500000 15.203586 21.936186 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
56 McKenzie North Dakota 38 053 38053 38 53 38053 0 13.706140 ... 0.047000 0.302126 0.369761 0.363755 0.370844 11.775148 5.100000 5.312158 9.595655 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
58 Richland Montana 30 083 30083 30 83 30083 0 0.000000 ... 0.037327 0.289600 0.350725 0.336799 0.364463 9.497645 6.600000 5.822862 10.223426 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
123 Bayfield Wisconsin 55 007 55007 55 7 55007 0 2.798769 ... 0.207025 0.301325 0.369321 0.345692 0.360286 13.624070 5.500000 7.959937 10.115237 <pysal.cg.shapes.Polygon object at 0x7f30f6d7d...
135 Douglas Wisconsin 55 031 55031 55 31 55031 0 1.481218 ... 0.407108 0.231009 0.327774 0.335744 0.354634 12.177288 11.100000 13.410263 16.076343 <pysal.cg.shapes.Polygon object at 0x7f30f6d7d...
148 Emmons North Dakota 38 029 38029 38 29 38029 0 0.000000 ... 0.000000 0.338304 0.457558 0.436659 0.415981 8.798283 5.200000 5.485790 5.089606 <pysal.cg.shapes.Polygon object at 0x7f30f6d7d...
153 Richland North Dakota 38 077 38077 38 77 38077 0 0.000000 ... 0.115715 0.319899 0.400422 0.344135 0.351533 11.422890 6.800000 6.506633 7.806045 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
166 Sioux North Dakota 38 085 38085 38 85 38085 0 27.307482 ... 0.079766 0.360412 0.411157 0.412360 0.430380 18.005952 16.800000 23.726542 30.778443 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
167 Pine Minnesota 27 115 27115 27 115 27115 0 1.960323 ... 1.655380 0.311982 0.365794 0.349111 0.364783 11.578691 6.000000 7.830749 9.040259 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
176 Sargent North Dakota 38 081 38081 38 81 38081 0 4.861921 ... 0.021983 0.315417 0.351081 0.332968 0.347183 10.234279 4.700000 4.912517 4.880000 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
178 Adams North Dakota 38 001 38001 38 1 38001 0 7.492320 ... 0.094518 0.285223 0.373231 0.362361 0.365218 9.879963 6.700000 4.732510 4.446978 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
181 Clatsop Oregon 41 007 41007 41 7 41007 0 3.652301 ... 0.342332 0.257003 0.346553 0.370412 0.372314 9.032872 8.800000 11.249418 11.210713 <pysal.cg.shapes.Polygon object at 0x7f30f6d1c...
183 Columbia Oregon 41 009 41009 41 9 41009 0 0.000000 ... 0.111830 0.247079 0.332061 0.331716 0.344570 7.871321 6.400000 8.386444 9.064105 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
185 Washburn Wisconsin 55 129 55129 55 129 55129 0 3.235932 ... 0.181528 0.320465 0.380816 0.350916 0.365397 9.709821 7.500000 8.945946 9.724389 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
186 Sawyer Wisconsin 55 113 55113 55 113 55113 0 3.518030 ... 0.126930 0.346035 0.398931 0.371229 0.393855 12.310838 7.900000 9.197652 12.565185 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
188 Kanabec Minnesota 27 065 27065 27 65 27065 0 0.000000 ... 0.156226 0.340374 0.378902 0.363335 0.354597 9.789289 4.600000 7.416340 9.420916 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
191 Grant Minnesota 27 051 27051 27 51 27051 0 0.000000 ... 0.048031 0.352016 0.391299 0.391685 0.371816 9.951563 6.200000 5.528134 6.990179 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
192 Douglas Minnesota 27 041 27041 27 41 27041 0 1.563991 ... 0.048825 0.333053 0.388476 0.359072 0.354371 9.718861 6.100000 5.876011 9.105939 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
200 Campbell South Dakota 46 021 46021 46 21 46021 0 0.000000 ... 0.101781 0.294651 0.473930 0.398838 0.409888 8.041958 4.300000 2.743902 5.525847 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
203 Perkins South Dakota 46 105 46105 46 105 46105 0 0.000000 ... 0.152594 0.302733 0.370142 0.400772 0.406949 10.570236 4.900000 5.159332 7.464029 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
204 Corson South Dakota 46 031 46031 46 31 46031 0 11.498218 ... 0.023838 0.395271 0.417271 0.447638 0.464924 13.469388 12.200000 19.453925 18.470705 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
205 Brown South Dakota 46 013 46013 46 13 46013 0 0.977345 ... 0.126476 0.278615 0.364344 0.347406 0.362180 8.962435 6.700000 10.357545 10.634667 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
207 Marshall South Dakota 46 091 46091 46 91 46091 0 0.000000 ... 0.020644 0.360634 0.400457 0.412790 0.341992 12.916919 6.800000 6.903485 10.254545 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
208 Roberts South Dakota 46 109 46109 46 109 46109 0 0.000000 ... 0.000000 0.346295 0.382736 0.397156 0.365133 13.991637 9.300000 11.360347 14.564220 <pysal.cg.shapes.Polygon object at 0x7f30f6d35...
216 Tillamook Oregon 41 057 41057 41 57 41057 0 0.000000 ... 0.180807 0.255654 0.366067 0.370520 0.375534 6.600249 6.800000 8.000000 10.445818 <pysal.cg.shapes.Polygon object at 0x7f30f6d52...
217 Washington Oregon 41 067 41067 41 67 41067 0 0.722776 ... 0.660560 0.263289 0.312470 0.336796 0.332442 7.043366 6.500000 10.856225 11.681524 <pysal.cg.shapes.Polygon object at 0x7f30f6d52...
218 Pope Minnesota 27 121 27121 27 121 27121 0 2.797829 ... 0.046533 0.339319 0.434278 0.385814 0.361487 10.869565 5.500000 5.548589 6.477462 <pysal.cg.shapes.Polygon object at 0x7f30f6d52...
219 Stevens Minnesota 27 149 27149 27 149 27149 0 0.000000 ... 0.545420 0.316610 0.398690 0.387275 0.345542 10.581614 8.200000 6.896552 7.704785 <pysal.cg.shapes.Polygon object at 0x7f30f6d52...
220 Isanti Minnesota 27 059 27059 27 59 27059 0 2.463661 ... 0.262336 0.303570 0.322182 0.324756 0.326085 10.605053 4.700000 7.055676 9.428738 <pysal.cg.shapes.Polygon object at 0x7f30f6d52...
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2976 Ozaukee Wisconsin 55 089 55089 55 89 55089 0 0.000000 ... 0.675537 0.234187 0.298030 0.322881 0.309077 7.243610 5.000000 7.275132 7.827655 <pysal.cg.shapes.Polygon object at 0x7f30f616b...
2978 Montcalm Michigan 26 117 26117 26 117 26117 0 0.000000 ... 1.809307 0.267063 0.331361 0.350614 0.367303 8.278038 8.800000 10.001549 12.603104 <pysal.cg.shapes.Polygon object at 0x7f30f616b...
2979 Gratiot Michigan 26 057 26057 26 57 26057 0 0.000000 ... 0.841414 0.267800 0.336064 0.349041 0.366099 8.387523 6.700000 9.668773 12.715206 <pysal.cg.shapes.Polygon object at 0x7f30f617d...
2992 Waukesha Wisconsin 55 133 55133 55 133 55133 0 0.421277 ... 0.359680 0.236245 0.285892 0.311858 0.293136 6.200273 5.500000 7.042424 7.900712 <pysal.cg.shapes.Polygon object at 0x7f30f617d...
2993 Milwaukee Wisconsin 55 079 55079 55 79 55079 0 2.348684 ... 20.376847 0.235310 0.315604 0.346722 0.372418 11.726036 11.800000 18.388856 24.152033 <pysal.cg.shapes.Polygon object at 0x7f30f617d...
2998 Ionia Michigan 26 067 26067 26 67 26067 0 1.545643 ... 5.266204 0.250648 0.314598 0.329620 0.340380 8.983935 7.300000 10.713448 11.267807 <pysal.cg.shapes.Polygon object at 0x7f30f617d...
2999 Clinton Michigan 26 037 26037 26 37 26037 0 0.877909 ... 0.376622 0.247049 0.293378 0.304213 0.323463 7.738420 6.200000 8.116178 8.420525 <pysal.cg.shapes.Polygon object at 0x7f30f617d...
3012 Livingston Michigan 26 093 26093 26 93 26093 0 2.615542 ... 0.581953 0.255442 0.310542 0.319606 0.306344 7.793457 5.900000 7.630845 8.207732 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3013 Ingham Michigan 26 065 26065 26 65 26065 0 2.208592 ... 9.874358 0.246190 0.327483 0.357029 0.377360 9.407645 9.800000 16.108398 18.999556 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3027 Winnebago Illinois 17 201 17201 17 201 17201 0 1.747988 ... 9.195257 0.236711 0.309030 0.329945 0.353680 8.992208 8.700000 11.564373 14.894016 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3029 Boone Illinois 17 007 17007 17 7 17007 0 0.000000 ... 0.412257 0.253596 0.293480 0.310248 0.336090 8.360374 5.600000 9.354671 9.989411 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3032 Washtenaw Michigan 26 161 26161 26 161 26161 0 1.546432 ... 11.210976 0.262129 0.326520 0.348523 0.344325 8.680991 8.400000 13.761024 14.479926 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3033 Jackson Michigan 26 075 26075 26 75 26075 0 1.767757 ... 8.001683 0.240779 0.320284 0.334497 0.364356 9.581740 9.100000 12.481712 15.768534 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3034 Kalamazoo Michigan 26 077 26077 26 77 26077 0 0.589234 ... 8.897950 0.239612 0.321951 0.347419 0.368107 9.041765 8.600000 13.527786 16.040024 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3035 Calhoun Michigan 26 025 26025 26 25 26025 0 4.080908 ... 10.577135 0.247517 0.320665 0.341298 0.374888 10.034801 10.400000 14.749644 18.371336 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3036 Van Buren Michigan 26 159 26159 26 159 26159 0 2.755106 ... 6.694262 0.269924 0.352243 0.352916 0.373401 9.367150 9.600000 11.824306 15.329981 <pysal.cg.shapes.Polygon object at 0x7f30f618f...
3042 Ogle Illinois 17 141 17141 17 141 17141 0 0.874753 ... 0.143613 0.279922 0.311374 0.317465 0.321674 7.476356 6.700000 7.287004 8.666252 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3045 Kane Illinois 17 089 17089 17 89 17089 0 0.960403 ... 5.986689 0.234401 0.290364 0.319262 0.326945 9.189593 7.800000 10.994243 12.065605 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3046 De Kalb Illinois 17 037 17037 17 37 17037 0 1.289142 ... 2.654879 0.260709 0.322043 0.325077 0.329307 9.609443 6.000000 8.868464 10.425777 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3050 Branch Michigan 26 023 26023 26 23 26023 0 1.910055 ... 1.698713 0.272805 0.332705 0.334908 0.364084 8.985926 7.900000 10.357504 13.506704 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3051 St. Joseph Michigan 26 149 26149 26 149 26149 0 0.000000 ... 2.715869 0.252413 0.328361 0.332476 0.351677 9.246945 8.000000 10.236528 13.184309 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3052 Cass Michigan 26 027 26027 26 27 26027 0 4.512798 ... 7.528751 0.242594 0.319415 0.338923 0.357145 8.350438 7.900000 10.735950 12.527013 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3055 Warren Pennsylvania 42 123 42123 42 123 42123 0 0.000000 ... 0.117647 0.233453 0.315722 0.325552 0.340837 10.086533 8.500000 9.093767 10.182685 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3057 McKean Pennsylvania 42 083 42083 42 83 42083 0 0.611430 ... 1.103308 0.226841 0.321510 0.331996 0.359109 12.217449 9.700000 11.220516 13.938297 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3059 Du Page Illinois 17 043 17043 17 43 17043 0 0.850723 ... 1.978083 0.241338 0.272934 0.303349 0.283952 6.010936 5.200000 8.037908 9.411177 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3065 La Porte Indiana 18 091 18091 18 91 18091 0 1.051403 ... 8.947752 0.234998 0.318773 0.325919 0.355624 10.369099 8.800000 11.754477 14.382948 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3068 Lagrange Indiana 18 087 18087 18 87 18087 0 1.917913 ... 0.149269 0.314581 0.327833 0.364168 0.338592 6.687440 4.900000 6.037441 6.376340 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3069 Steuben Indiana 18 151 18151 18 151 18151 0 1.939789 ... 0.185819 0.273650 0.365597 0.344738 0.315165 7.203579 4.900000 7.279808 7.972756 <pysal.cg.shapes.Polygon object at 0x7f30f611f...
3074 Fairfax Virginia 51 059 51059 51 59 51059 1 2.181802 ... 7.670072 0.240832 0.294093 0.315353 0.262649 5.649105 6.590806 10.452406 11.045038 <pysal.cg.shapes.Polygon object at 0x7f30f6130...
3079 Oconto Wisconsin 55 083 55083 55 83 55083 0 1.121183 ... 0.084183 0.305334 0.369350 0.352464 0.357623 10.208161 5.943685 7.907155 9.629781 <pysal.cg.shapes.Polygon object at 0x7f30f6130...

726 rows × 70 columns

Distance

There are many other kinds of weighting functions in PySAL. Another separate type use a continuous measure of distance to define neighborhoods. To use these measures, we first must extract the polygons' centroids.

For each polygon poly in dataframe.geometry, we want poly.centroid. So, one way to do this is to make a list of all of the centroids:


In [47]:
centroids = [list(poly.centroid) for poly in dataframe.geometry]

In [48]:
centroids[0:5] #let's look at the first five


Out[48]:
[[-94.90336786329912, 48.771730563701574],
 [-118.51718120712802, 48.46959353253665],
 [-117.85532452342407, 48.39591039096631],
 [-119.73943524482668, 48.5484338901435],
 [-117.27400489644516, 48.53279719845048]]

If we were working with point data, this step would be unncessary.

KnnW

If we wanted to consider only the k-nearest neighbors to an observation's centroid, we could use the knnW function in PySAL.

This specific type of distance weights requires that we first build a KDTree, a special representation for spatial point data. Fortunately, this is built in to PySAL:


In [50]:
kdtree = ps.cg.KDTree(centroids)

Then, we can use this to build a spatial weights object where only the closest k observations are considered "neighbors." In this example, let's do the closest 5:


In [51]:
nn5 = ps.knnW(kdtree, k=5)

In [52]:
nn5.histogram


Out[52]:
[(5, 3085)]

So, all observations have exactly 5 neighbors. Sometimes, these neighbors are actually different observations than the ones identified by contiguity neighbors.

For example, Pend Oreille gets a new neighbor, Kootenai county:


In [53]:
nn5[4]


Out[53]:
{2: 1.0, 5: 1.0, 28: 1.0, 62: 1.0, 65: 1.0}

In [54]:
dataframe.loc[nn5.neighbors[4] + [4]]


Out[54]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
2 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
28 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
5 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
62 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
65 Kootenai Idaho 16 055 16055 16 55 16055 0 0.000000 ... 0.134680 0.234269 0.343077 0.357464 0.365750 9.152454 8.8 9.351636 10.602422 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...

6 rows × 70 columns


In [55]:
fips_frame.loc[qW.neighbors['53051'] + ['53051']]


Out[55]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
FIPS
53065 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
16017 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
16021 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
53063 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
53051 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...

5 rows × 70 columns

Kernel W

Kernel Weights are continuous distance-based weights that use kernel densities to provide an indication of neighborliness.

Typically, they estimate a bandwidth, which is a parameter governing how far out observations should be considered neighboring. Then, using this bandwidth, they evaluate a continuous kernel function to provide a weight between 0 and 1.

Many different choices of kernel functions are supported, and bandwidth can be estimated at each point or over the entire map.

For example, if we wanted to use a single estimated bandwidth for the entire map and weight according to a gaussian kernel:


In [68]:
kernelW = ps.Kernel(centroids, fixed=True, function='gaussian')
#ps.Kernel(centroids, fixed=False, function='gaussian') #same kernel, but bandwidth changes at each observation

In [69]:
dataframe.loc[kernelW.neighbors[4] + [4]]


Out[69]:
NAME STATE_NAME STATE_FIPS CNTY_FIPS FIPS STFIPS COFIPS FIPSNO SOUTH HR60 ... BLK90 GI59 GI69 GI79 GI89 FH60 FH70 FH80 FH90 geometry
1 Ferry Washington 53 019 53019 53 19 53019 0 0.000000 ... 0.317712 0.256158 0.360665 0.361928 0.360640 10.053476 2.6 10.079576 11.397059 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
69 Lincoln Washington 53 043 53043 53 43 53043 0 3.052783 ... 0.169224 0.269758 0.336764 0.370721 0.368230 6.803193 4.7 4.343144 6.250000 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
2 Stevens Washington 53 065 53065 53 65 53065 0 1.863863 ... 0.210030 0.283999 0.394083 0.357566 0.369942 9.258437 5.6 6.812127 10.352015 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
110 Whitman Washington 53 075 53075 53 75 53075 0 1.066223 ... 1.263701 0.261340 0.358924 0.355758 0.381245 6.929178 4.8 7.927677 9.189189 <pysal.cg.shapes.Polygon object at 0x7f30f6d63...
62 Spokane Washington 53 063 53063 53 63 53063 0 2.514973 ... 1.412703 0.251340 0.346762 0.356968 0.370488 10.099520 10.2 12.687751 15.229159 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
65 Kootenai Idaho 16 055 16055 16 55 16055 0 0.000000 ... 0.134680 0.234269 0.343077 0.357464 0.365750 9.152454 8.8 9.351636 10.602422 <pysal.cg.shapes.Polygon object at 0x7f30f6dc1...
100 Benewah Idaho 16 009 16009 16 9 16009 0 0.000000 ... 0.075595 0.247637 0.339068 0.337666 0.354479 8.701299 3.9 7.240618 8.512545 <pysal.cg.shapes.Polygon object at 0x7f30f6d63...
28 Bonner Idaho 16 017 16017 16 17 16017 0 2.138534 ... 0.138983 0.261589 0.367973 0.370304 0.375177 8.661813 7.1 8.324631 9.571821 <pysal.cg.shapes.Polygon object at 0x7f30f6da0...
5 Boundary Idaho 16 021 16021 16 21 16021 0 0.000000 ... 0.036006 0.261939 0.350351 0.355913 0.340525 7.112971 6.8 8.249497 9.343201 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...
4 Pend Oreille Washington 53 051 53051 53 51 53051 0 0.000000 ... 0.134605 0.243263 0.365614 0.358706 0.387848 8.243930 4.1 7.557643 10.313002 <pysal.cg.shapes.Polygon object at 0x7f30f65a0...

11 rows × 70 columns

As you can see, this provides our target observation, Pend Oreille, with many new neighbors. \


In [ ]: